﻿using Microsoft.Win32;
using ScottPlot;
using ScottPlot.Plottables;
using System.Diagnostics;
using System.Diagnostics.Metrics;
using System.IO;
using System.Reflection;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace CurrentTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        MainViewModel vm = new MainViewModel();
        public MainWindow()
        {
            InitializeComponent();
            DataContext = vm;
            InitPlot();
        }

        private string ConvertValueToCurrentString(double value)
        {
            double abs = Math.Abs(value);
            if (abs < 1000)
            {
                return $"{value}uA";
            }
            else if (abs < 1000000)
            {
                return $"{(value / 1000):N3}mA";
            }
            else
            {
                return $"{(value / 1000000):N3}A";
            }
        }

        private CancellationTokenSource cancellationTokenSource = new();
        private int renderIndex = 0;
        private async void Window_Loaded(object sender, RoutedEventArgs e)
        {
            if (signal == null) return;
            if (signalFilter == null) return;
            DateTime startTime = DateTime.Now;
            DateTime now = DateTime.Now;
            int count = 0;
            while (!cancellationTokenSource.IsCancellationRequested) 
            {
                try
                {
                    await foreach (var item in vm.DataChannel.Reader.ReadAllAsync(cancellationTokenSource.Token))
                    {
                        signal.MaxRenderIndex = renderIndex;
                        signalFilter.MaxRenderIndex = renderIndex;
                        //kf.Update(item);
                        
                        if (renderIndex >= data.Length)
                        {
                            renderIndex = data.Length - 1;
                            for(int i = 0; i < data.Length - 1; i++)
                            {
                                data[i] = data[i + 1];
                                filterData[i] = filterData[i + 1];
                            }
                        }
                        data[renderIndex] = item.Value;
                        filterData[renderIndex] = item.Filter;
                        renderIndex++;
                        now = DateTime.Now;
                        if((now - startTime).TotalMilliseconds >= 10)
                        {
                            await App.Current.Dispatcher.BeginInvoke(() => {
                                TextBlockMeasureValue.Text = ConvertValueToCurrentString(item.Value);
                                TextBlockFilterMeasureValue.Text = ConvertValueToCurrentString(item.Filter);
                            });
                            plot.Refresh();
                            startTime = now;
                        }
                        count++;
                        if(count % 1000 == 0)
                        {
                            //Debug.WriteLine(count);
                        }
                    }
                }
                catch { }
            }
            Debug.WriteLine("退出数据接收");
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            cancellationTokenSource.Cancel();
        }

        class KalmanFilter
        {
            // 估计值
            public double EstimatedValue { get; private set; }
            // 估计误差协方差
            public double EstimateErrorCovariance { get; private set; }
            // 状态转移矩阵
            private double StateTransitionMatrix;
            // 过程噪声协方差
            private double ProcessNoiseCovariance;
            // 观测矩阵
            private double ObservationMatrix;
            // 观测噪声协方差
            private double ObservationNoiseCovariance;
            // 卡尔曼增益
            private double KalmanGain;

            // 构造函数，用于初始化卡尔曼滤波器的各种参数
            public KalmanFilter(double initialEstimatedValue, double initialEstimateErrorCovariance,
                                 double stateTransitionMatrix, double processNoiseCovariance,
                                 double observationMatrix, double observationNoiseCovariance)
            {
                EstimatedValue = initialEstimatedValue;
                EstimateErrorCovariance = initialEstimateErrorCovariance;
                StateTransitionMatrix = stateTransitionMatrix;
                ProcessNoiseCovariance = processNoiseCovariance;
                ObservationMatrix = observationMatrix;
                ObservationNoiseCovariance = observationNoiseCovariance;
            }

            // 执行卡尔曼滤波的预测和更新步骤
            public void Update(double measurement)
            {
                // 预测步骤
                Predict();
                // 更新步骤
                UpdateWithMeasurement(measurement);
            }

            private void Predict()
            {
                // 根据状态转移矩阵更新估计值
                EstimatedValue = StateTransitionMatrix * EstimatedValue;
                // 更新估计误差协方差
                EstimateErrorCovariance = StateTransitionMatrix * EstimateErrorCovariance * StateTransitionMatrix + ProcessNoiseCovariance;
            }

            private void UpdateWithMeasurement(double measurement)
            {
                // 计算卡尔曼增益
                KalmanGain = EstimateErrorCovariance * ObservationMatrix / (ObservationMatrix * EstimateErrorCovariance * ObservationMatrix + ObservationNoiseCovariance);
                // 更新估计值，融合预测值和测量值
                EstimatedValue = EstimatedValue + KalmanGain * (measurement - ObservationMatrix * EstimatedValue);
                // 更新估计误差协方差
                EstimateErrorCovariance = (1 - KalmanGain * ObservationMatrix) * EstimateErrorCovariance;
            }
        }

        //class Program
        //{
        //    static void Main()
        //    {
        //        // 初始化卡尔曼滤波器参数
        //        double initialEstimatedValue = 0;
        //        double initialEstimateErrorCovariance = 10;
        //        double stateTransitionMatrix = 1;
        //        double processNoiseCovariance = 0.01;
        //        double observationMatrix = 1;
        //        double observationNoiseCovariance = 1;

        //        KalmanFilter kf = new KalmanFilter(initialEstimatedValue, initialEstimateErrorCovariance,
        //                                            stateTransitionMatrix, processNoiseCovariance,
        //                                            observationMatrix, observationNoiseCovariance);

        //        // 模拟一些带有噪声的测量数据
        //        double[] measurements = { 1.2, 1.5, 1.8, 1.7, 1.6 };

        //        foreach (double measurement in measurements)
        //        {
        //            kf.Update(measurement);
        //            Console.WriteLine($"Filtered estimate: {kf.EstimatedValue}");
        //        }
        //    }
        //}
        private static double initialEstimatedValue = 0;
        private static double initialEstimateErrorCovariance = 10;
        private static double stateTransitionMatrix = 1;
        private static double processNoiseCovariance = 0.01;
        private static double observationMatrix = 1;
        private static double observationNoiseCovariance = 1;
        private KalmanFilter kf = new KalmanFilter(initialEstimatedValue, initialEstimateErrorCovariance,
                                                    stateTransitionMatrix, processNoiseCovariance,
                                                    observationMatrix, observationNoiseCovariance);

        private double[] data = new double[600000];
        private double[] filterData = new double[600000];
        private Signal? signal;
        private Signal? signalFilter;
        private ScottPlot.Plottables.Crosshair ch;
        private void InitPlot()
        {
            signal = plot.Plot.Add.Signal(data);
            signal.MaxRenderIndex = 0;
            signalFilter = plot.Plot.Add.Signal(filterData);
            signalFilter.MaxRenderIndex = 0;
            ch = plot.Plot.Add.Crosshair(0, 0);
            ch.IsVisible = false;
            ch.MarkerShape = MarkerShape.OpenCircle;
            ch.MarkerSize = 15;
            //ch.TextColor = ScottPlot.Colors.White;
            //ch.TextBackgroundColor = ch.HorizontalLine.Color;
            plot.MouseMove += (s, e) =>
            {
                Point position = e.GetPosition((IInputElement)s);
                double x = position.X;
                double y = position.Y;
                Pixel mousePixel = new(x, y);
                Coordinates mouseCoordinates = plot.Plot.GetCoordinates(mousePixel);
                //this.Text = $"X={mouseCoordinates.X:N3}, Y={mouseCoordinates.Y:N3}";

                DataPoint nearest = signal.GetNearestX(mouseCoordinates, plot.Plot.LastRender);
                if (nearest.IsReal)
                {
                    ch.IsVisible = true;
                    ch.Position = nearest.Coordinates;
                    
                    Title = ConvertValueToCurrentString(nearest.Y);
                }

                // hide the crosshair when no point is selected
                if (!nearest.IsReal && ch.IsVisible)
                {
                    ch.IsVisible = false;
                    Title = $"";
                }
                //ch.Position = mouseCoordinates;
                //ch.VerticalLine.Text = $"{mouseCoordinates.X:N3}";
                //ch.HorizontalLine.Text = $"{mouseCoordinates.Y:N3}";
                
                plot.Refresh();
            };

            plot.MouseDown += (s, e) =>
            {
                Point position = e.GetPosition((IInputElement)s);
                double x = position.X;
                double y = position.Y;
                Pixel mousePixel = new(x, y);
                Coordinates mouseCoordinates = plot.Plot.GetCoordinates(mousePixel);
                //Text = $"X={mouseCoordinates.X:N3}, Y={mouseCoordinates.Y:N3} (mouse down)";
            };
        }
        private async void ButtonExport_Click(object sender, RoutedEventArgs e)
        {
            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.InitialDirectory = "C:\\";
            saveFileDialog.Filter = "CSV文件 (*.csv)|*.csv|所有文件 (*.*)|*.*";
            if (saveFileDialog.ShowDialog() == true)
            {
                string text = string.Join("\n", data.Take(renderIndex));
                await File.WriteAllTextAsync(saveFileDialog.FileName, text);
            }
        }

        private void ButtonOpen_Click(object sender, RoutedEventArgs e)
        {
            if (signal == null) return;
            renderIndex = 0;
            signal.MaxRenderIndex = 0;
            signalFilter.MaxRenderIndex = 0;
            plot.Refresh();
        }
    }
}